/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pt.utl.ist.isr.livetrack.server;

import pt.utl.ist.isr.livetrack.server.interfaces.CameraFactory;
import pt.utl.ist.isr.livetrack.server.impl.AxisCameraImpl;
import pt.utl.ist.isr.livetrack.server.impl.SequentialFileCameraImpl;
import pt.utl.ist.isr.livetrack.server.impl.TimestampedFileCameraImpl;
import pt.utl.ist.isr.livetrack.server.interfaces.Camera;
import gnu.cajo.invoke.Remote;
import gnu.cajo.utils.ItemServer;
import gnu.cajo.utils.extra.TransparentItemProxy;
import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.String;
import java.net.Inet4Address;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.util.HashMap;
import java.util.Map;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;
import pt.utl.ist.isr.livetrack.server.impl.PerimeterImpl;
import pt.utl.ist.isr.livetrack.server.impl.TrackerImpl;
import pt.utl.ist.isr.livetrack.server.interfaces.Perimeter;
import pt.utl.ist.isr.livetrack.server.interfaces.Tracker;

/**
 *
 * @author David Miguel Antunes <davidmiguel [ at ] antunes.net>
 */
public class LTServerImpl implements LTServer {

    private static final Logger logger = Logger.getLogger(LTServerImpl.class.getName());
    private CameraFactory cameraFactoryImpl = new CameraFactory() {

        @Override
        public void newAxisCamera(String id, String hostname) {
            Camera axis = new AxisCameraImpl(hostname, id);
            cameras.put(id, axis);
        }

        @Override
        public void newSequentialFileCamera(String id, String dir) {
            Camera file = new SequentialFileCameraImpl(id, dir);
            cameras.put(id, file);
        }

        @Override
        public void newTimestampedFileCamera(String id, String dir) {
            Camera file = new TimestampedFileCameraImpl(id, dir);
            cameras.put(id, file);
        }
    };
    private Map<String, Camera> cameras = new HashMap<String, Camera>();
    private PerimeterImpl perimeter = new PerimeterImpl();
    private TrackerImpl tracker = new TrackerImpl(this);
    private String hostname = "localhost";
    private int port = 1198;
    private String file = "LTServer.save";

    @Override
    public CameraFactory getCameraFactory() {
        return DistributionUtils.proxyObj(cameraFactoryImpl);
    }

    @Override
    public Perimeter getPerimeter() {
        return DistributionUtils.proxyObj(perimeter);
    }

    public PerimeterImpl getPerimeterLocal() {
        return perimeter;
    }

    @Override
    public void removeCamera(String id) {
        cameras.remove(id);
    }

    public static LTServer getInstance(String hostname, int port) throws RuntimeException {
        try {
            if (Inet4Address.getByName(hostname).isReachable(1000)) {
                LTServer instance = (LTServer) TransparentItemProxy.getItem(
                        "//" + hostname + ":"
                        + port + "/"
                        + "LTServer",
                        new Class[]{LTServer.class});
                instance.ping();
                return instance;
            }
        } catch (RemoteException ex) {
            logger.error(ex);
        } catch (NotBoundException ex) {
            logger.error(ex);
        } catch (IOException ex) {
            logger.error(ex);
        } catch (ClassNotFoundException ex) {
            logger.error(ex);
        } catch (InstantiationException ex) {
            logger.error(ex);
        } catch (IllegalAccessException ex) {
            logger.error(ex);
        }
        return null;
    }

    public static void main(String[] args) throws IOException {
        BasicConfigurator.configure();

        LTServerImpl impl = new LTServerImpl();
        if (args.length > 0) {
            impl.hostname = args[0];
        }
        if (args.length > 1) {
            impl.port = Integer.parseInt(args[1]);
        }
        if (args.length > 2) {
            impl.file = args[2];
        }
        Remote.config(impl.hostname, impl.port, null, 0);

        impl.loadFromFile();
        ItemServer.bind(impl, LTServer.class.getSimpleName());
        logger.info(LTServerImpl.class.getSimpleName() + " started on //" + Remote.getDefaultServerHost() + ":" + Remote.getDefaultServerPort());

//        impl.getCameraFactory().newAxisCamera("my_axis", "extcam-15.se.axis.com");
    }

    public void ping() {
        logger.trace("Received ping");
    }

    @Override
    public Map<String, Camera> getCameras() {
        return DistributionUtils.proxyMap(cameras);
    }

    public Map<String, Camera> getCamerasLocal() {
        return cameras;
    }

    @Override
    public Tracker getTracker() {
        return tracker;
    }

    @Override
    public String getHostname() {
        return hostname;
    }

    @Override
    public int getPort() {
        return port;
    }

    public String getFile() {
        return file;
    }

    public void setFile(String file) {
        this.file = file;
    }

    public void setHostname(String hostname) {
        this.hostname = hostname;
    }

    public void setPort(int port) {
        this.port = port;
    }

    @Override
    public void saveToFile() {
        try {
            XMLEncoder encoder = new XMLEncoder(new FileOutputStream(file));
            encoder.writeObject(cameras);
            perimeter.writeToFile(encoder);
            encoder.writeObject(tracker);
            encoder.close();
        } catch (FileNotFoundException ex) {
            logger.error(ex);
        }
    }

    private void loadFromFile() {
        try {
            XMLDecoder decoder = new XMLDecoder(new FileInputStream(file));
            cameras = (Map<String, Camera>) decoder.readObject();
            perimeter = PerimeterImpl.createFromFile(decoder);
            tracker = (TrackerImpl) decoder.readObject();
            tracker.setServer(this);
            decoder.close();
        } catch (FileNotFoundException ex) {
            logger.error(ex);
        } catch (Exception e) {
            logger.error(e);
        }
    }
}
